﻿using System;
using System.Net;
using System.Threading;
using System.Windows;
using NLog;
using Newtonsoft.Json.Linq;
using System.Threading.Tasks;

namespace KlavoNotifications.Fetch
{
    public class Fetcher
    {
        private static readonly Logger _logger = LogManager.GetCurrentClassLogger();

        private bool _isStarted;
        public delegate void FetchDataHandler(FetchData data);
        public event FetchDataHandler FetchDataComleted = delegate { };

        void OnFetchDataComleted(FetchData data)
        {
            FetchDataHandler handler = FetchDataComleted;
            if (handler != null) handler(data);
        }

        public bool IsStarted
        {
            get { return _isStarted; }
        }

        public void Start()
        {
            _isStarted = true;

            Task.Factory.StartNew(Queue);
        }

        public void Stop()
        {
            _isStarted = false;
        }

        private void Queue()
        {
            while (_isStarted)
            {
                FetchData data = Fetch();
                if (data != null)
                {
                    Application.Current.Dispatcher.BeginInvoke(new Action(() => OnFetchDataComleted(data)));
                }

                Thread.Sleep(3500);
            }
        }

        private string DownloadJson()
        {
            String result;
            try
            {
                WebClient wc = new WebClient();
                wc.Headers.Add("User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)");
                result = wc.DownloadString("http://klavogonki.ru/gamelist.data");
            }
            catch (Exception exc)
            {
                _logger.ErrorException("DownloadJson", exc);
                return String.Empty;
            }

            return result;
        }

        private FetchData Fetch()
        {
            FetchData result = new FetchData();

            string raw = DownloadJson();

            if (String.IsNullOrEmpty(raw))
                return null;

            JObject j = JObject.Parse(raw);
            JToken time = j["time"];
            JToken gamelist = j["gamelist"];

            if (time.Type == JTokenType.Integer)
                result.Timestamp = time.Value<int>();

            foreach (var jGame in gamelist)
            {
                if (jGame["type"].ToString() == "open")
                {
                    Game game = new Game();

                    game.Id = IntFromJtoken(jGame["id"]);
                    game.Timeout = StringFromJToken(jGame["params"]["timeout"]);
                    game.Type = StringFromJToken(jGame["params"]["type"]);

                    string gameType = StringFromJToken(jGame["params"]["gametype"]);
                    if (!String.IsNullOrEmpty(gameType))
                    {
                        game.GameType = GameTypeFromString(gameType);
                    }

                    FillGameWithVocInfo(jGame, game);

                    // "regular_competition": 5,

                    game.Competition = StringFromJToken(jGame["params"]["competition"]);
                    game.Mode = StringFromJToken(jGame["params"]["mode"]);

                    JToken competition = jGame["params"]["regular_competition"];
                    game.IsRegularCompetition = competition != null;
                    if (game.IsRegularCompetition)
                        game.RegularCompetitionX = IntFromJtoken(competition);

                    game.BeginTime = IntFromJtoken(jGame["begintime"]);

                    JToken allowed = jGame["allowed"];
                    if (allowed != null && allowed.Type == JTokenType.Boolean)
                        game.Allowed = allowed.Value<bool>();

                    foreach (var jPlayer in jGame["players"])
                    {
                        Player player = new Player();
                        player.Name = StringFromJToken(jPlayer["name"]);
                        game.Players.Add(player);
                    }

                    game.Data = jGame.ToString();
                    game.Token = jGame;

                    //exclude not valid game
                    if (game.BeginTime == 0 && String.IsNullOrEmpty(game.Timeout))
                        continue;

                    result.Games.Add(game);
                }
            }

            return result;
        }

        GameType GameTypeFromString(string gameType)
        {
            switch (gameType)
            {
                case "normal":
                    return GameType.Normal;
                case "noerror":
                    return GameType.NoErrors;
                case "chars":
                    return GameType.Chars;
                case "marathon":
                    return GameType.Marathon;
                case "digits":
                    return GameType.Digits;
                case "sprint":
                    return GameType.Sprint;
                case "abra":
                    return GameType.Abra;
                case "referats":
                    return GameType.Referats;
                default:
                    if (gameType.StartsWith("voc-"))
                    {
                        return GameType.CustomVoc;
                    }
                    else
                    {
                        throw new Exception("todo game type");
                    }
                    break;
            }
        }

        void FillGameWithVocInfo(JToken jGame, Game game)
        {
            var jVoc = jGame["params"]["voc"];
            if (jVoc != null)
            {
                if (jVoc.Type == JTokenType.Integer)
                    game.VocId = jVoc.Value<int>();

                if (jVoc.Type == JTokenType.Object)
                {
                    Voc voc = new Voc();
                    voc.Id = IntFromJtoken(jVoc["id"]);
                    voc.Name = StringFromJToken(jVoc["name"]);
                    voc.Desc = StringFromJToken(jVoc["description"]);
                    game.Voc = voc;
                }
            }
        }

        string StringFromJToken(JToken token)
        {
            return token == null ? String.Empty : token.ToString();
        }

        int IntFromJtoken(JToken token)
        {
            if (token == null || token.Type != JTokenType.Integer)
                return 0;

            return token.Value<int>();
        }
    }
}